#pragma once

#include <AVUIDependencyObject.h>
#include <AVUIISealable.h>

namespace AVUI {

typedef ItemStructList< TRefCountedPtr<Delegate> > EventStorage;

/// <summary>Defines an object that has a modifiable state and a read-only (frozen) state. Classes that derive from <see cref="T:System.Windows.Freezable" /> provide detailed change notification, can be made immutable, and can clone themselves. </summary>
class Freezable : public DependencyObject, public ISealable
{
public:

    DECLARE_ELEMENT(Freezable, DependencyObject);

#pragma region Public Methods

    /// <summary>Creates a modifiable clone of the <see cref="T:System.Windows.Freezable" />, making deep copies of the object's values. When copying the object's dependency properties, this method copies expressions (which might no longer resolve) but not animations or their current values. </summary>
    /// <returns>A modifiable clone of the current object. The cloned object's <see cref="P:System.Windows.Freezable.IsFrozen" /> property is false even if the source's <see cref="P:System.Windows.Freezable.IsFrozen" /> property is true.</returns>
    TRefCountedPtr<Freezable> Clone();

    /// <summary>Creates a modifiable clone (deep copy) of the <see cref="T:System.Windows.Freezable" /> using its current values.</summary>
    /// <returns>A modifiable clone of the current object. The cloned object's <see cref="P:System.Windows.Freezable.IsFrozen" /> property is false even if the source's <see cref="P:System.Windows.Freezable.IsFrozen" /> property is true.</returns>
    TRefCountedPtr<Freezable> CloneCurrentValue();

    /// <summary>Makes the current object unmodifiable and sets its <see cref="P:System.Windows.Freezable.IsFrozen" /> property to true. </summary>
    /// <exception cref="T:System.InvalidOperationException">The <see cref="T:System.Windows.Freezable" /> cannot be made unmodifiable. </exception>
    void Freeze();

    /// <summary>Creates a frozen copy of the <see cref="T:System.Windows.Freezable" />, using base (non-animated) property values. Because the copy is frozen, any frozen sub-objects are copied by reference. </summary>
    /// <returns>A frozen copy of the <see cref="T:System.Windows.Freezable" />. The copy's <see cref="P:System.Windows.Freezable.IsFrozen" /> property is set to true. </returns>
    /// <exception cref="T:System.InvalidOperationException">The <see cref="T:System.Windows.Freezable" /> cannot be frozen because it contains expressions or animated properties.</exception>
    TRefCountedPtr<Freezable> GetAsFrozen();

    /// <summary>Creates a frozen copy of the <see cref="T:System.Windows.Freezable" /> using current property values. Because the copy is frozen, any frozen sub-objects are copied by reference.</summary>
    /// <returns>A frozen copy of the <see cref="T:System.Windows.Freezable" />. The copy's <see cref="P:System.Windows.Freezable.IsFrozen" /> property is set to true.</returns>
    TRefCountedPtr<Freezable> GetCurrentValueAsFrozen();

#pragma endregion Public Methods

#pragma region Public Properties

    /// <summary>Gets a value that indicates whether the object can be made unmodifiable. </summary>
    /// <returns>true if the current object can be made unmodifiable or is already unmodifiable; otherwise, false.</returns>
    bool get_CanFreeze();
    DECLARE_READONLY_PROPERTY(CanFreeze, bool, false);
    
    /// <summary>Gets a value that indicates whether the object is currently modifiable. </summary>
    /// <returns>true if the object is frozen and cannot be modified; false if the object can be modified.</returns>
    bool get_IsFrozen();
    DECLARE_READONLY_PROPERTY(IsFrozen, bool, false);

#pragma endregion Public Properties

#pragma region Public Events

    /// <summary>Occurs when the <see cref="T:System.Windows.Freezable" /> or an object it contains is modified. </summary>
    void add_Changed(EventHandler* pValue) { WritePreamble(); add_ChangedInternal(pValue); }
    void remove_Changed(EventHandler* pValue) { WritePreamble(); remove_ChangedInternal(pValue); }
    DECLARE_EVENT(Changed);

#pragma endregion Public Events

protected:

#pragma region Protected Methods

    /// <summary>Initializes a new instance of a <see cref="T:System.Windows.Freezable" /> derived class. </summary>
    Freezable() { }

    /// <summary>Makes the instance a clone (deep copy) of the specified <see cref="T:System.Windows.Freezable" /> using base (non-animated) property values. </summary>
    /// <param name="sourceFreezable">The object to clone.</param>
    virtual void CloneCore(Freezable* pSourceFreezable);

    /// <summary>Makes the instance a modifiable clone (deep copy) of the specified <see cref="T:System.Windows.Freezable" /> using current property values.</summary>
    /// <param name="sourceFreezable">The <see cref="T:System.Windows.Freezable" /> to be cloned.</param>
    virtual void CloneCurrentValueCore(Freezable* pSourceFreezable);

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.Freezable" /> class. </summary>
    /// <returns>The new instance.</returns>
    TRefCountedPtr<Freezable> CreateInstance();

    /// <summary>When implemented in a derived class, creates a new instance of the <see cref="T:System.Windows.Freezable" /> derived class. </summary>
    /// <returns>The new instance.</returns>
    virtual TRefCountedPtr<Freezable> CreateInstanceCore() = 0;
 
    /// <summary>If the <paramref name="isChecking" /> parameter is true, this method indicates whether the specified <see cref="T:System.Windows.Freezable" /> can be made unmodifiable. If the <paramref name="isChecking" /> parameter is false, this method attempts to make the specified <see cref="T:System.Windows.Freezable" /> unmodifiable and indicates whether the operation succeeded.</summary>
    /// <returns>If <paramref name="isChecking" /> is true, this method returns true if the specified <see cref="T:System.Windows.Freezable" /> can be made unmodifiable, or false if it cannot be made unmodifiable. If <paramref name="isChecking" /> is false, this method returns true if the specified <see cref="T:System.Windows.Freezable" /> is now unmodifiable, or false if it cannot be made unmodifiable. </returns>
    /// <param name="freezable">The object to check or make unmodifiable. If <paramref name="isChecking" /> is true, the object is checked to determine whether it can be made unmodifiable. If <paramref name="isChecking" /> is false, the object is made unmodifiable, if possible.</param>
    /// <param name="isChecking">true to return an indication of whether the object can be frozen (without actually freezing it); false to actually freeze the object.</param>
    /// <exception cref="T:System.InvalidOperationException">When <paramref name="isChecking" /> is false, the attempt to make <paramref name="freezable" /> unmodifiable was unsuccessful; the object is now in an unknown state (it might be partially frozen).  </exception>
    static bool Freeze(Freezable* pFreezable, bool isChecking);

    /// <summary>Makes the <see cref="T:System.Windows.Freezable" /> object unmodifiable or tests whether it can be made unmodifiable.</summary>
    /// <returns>If <paramref name="isChecking" /> is true, this method returns true if the <see cref="T:System.Windows.Freezable" /> can be made unmodifiable, or false if it cannot be made unmodifiable. If <paramref name="isChecking" /> is false, this method returns true if the if the specified <see cref="T:System.Windows.Freezable" /> is now unmodifiable, or false if it cannot be made unmodifiable. </returns>
    /// <param name="isChecking">true to return an indication of whether the object can be frozen (without actually freezing it); false to actually freeze the object.</param>
    virtual bool FreezeCore(bool isChecking);

    /// <summary>Makes the instance a frozen clone of the specified <see cref="T:System.Windows.Freezable" /> using base (non-animated) property values.</summary>
    /// <param name="sourceFreezable">The instance to copy.</param>
    virtual void GetAsFrozenCore(Freezable* pSourceFreezable);

    /// <summary>Makes the current instance a frozen clone of the specified <see cref="T:System.Windows.Freezable" />. If the object has animated dependency properties, their current animated values are copied.</summary>
    /// <param name="sourceFreezable">The <see cref="T:System.Windows.Freezable" /> to copy and freeze.</param>
    virtual void GetCurrentValueAsFrozenCore(Freezable* pSourceFreezable);

     /// <summary>Called when the current <see cref="T:System.Windows.Freezable" /> object is modified. </summary>
    /// <returns />
    virtual void OnChanged() { };

    /// <summary>Ensures that appropriate context pointers are established for a <see cref="T:System.Windows.DependencyObjectType" /> data member that has just been set.</summary>
    /// <param name="oldValue">The previous value of the data member.</param>
    /// <param name="newValue">The current value of the data member.</param>
    void OnFreezablePropertyChanged(DependencyObject* pOldValue, DependencyObject* pNewValue);

    /// <summary>This member supports the Windows Presentation Foundation (WPF) infrastructure and is not intended to be used directly from your code.</summary>
    /// <param name="oldValue" />
    /// <param name="newValue" />
    /// <param name="property" />
    void OnFreezablePropertyChanged(DependencyObject* pOldValue, DependencyObject* pNewValue, DependencyProperty* pProperty);

    /// <summary>Overrides the <see cref="T:System.Windows.DependencyObject" /> implementation of <see cref="M:System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs)" /> to also invoke any <see cref="E:System.Windows.Freezable.Changed" /> handlers in response to a changing dependency property of type <see cref="T:System.Windows.Freezable" />.</summary>
    /// <param name="e">Event data that contains information about which property changed, and its old and new values.</param>
    virtual void OnPropertyChanged(DependencyPropertyChangedEventArgs* e) AVUI_OVERRIDE;

    /// <summary>Ensures that the <see cref="T:System.Windows.Freezable" /> is being accessed from a valid thread. Inheritors of <see cref="T:System.Windows.Freezable" /> must call this method at the beginning of any API that reads data members that are not dependency properties.</summary>
    void ReadPreamble();

    /// <summary>Raises the <see cref="E:System.Windows.Freezable.Changed" /> event for the <see cref="T:System.Windows.Freezable" /> and invokes its <see cref="M:System.Windows.Freezable.OnChanged" /> method. Classes that derive from <see cref="T:System.Windows.Freezable" /> should call this method at the end of any API that modifies class members that are not stored as dependency properties.</summary>
    /// <returns />
    void WritePostscript();

    /// <summary>Verifies that the <see cref="T:System.Windows.Freezable" /> is not frozen and that it is being accessed from a valid threading context. <see cref="T:System.Windows.Freezable" /> inheritors should call this method at the beginning of any API that writes to data members that are not dependency properties. </summary>
    /// <returns />
    /// <exception cref="T:System.InvalidOperationException">The <see cref="T:System.Windows.Freezable" /> instance is frozen and cannot have its members written to.</exception>
    void WritePreamble();

#pragma endregion Protected Methods

public:

#pragma region Internal Methods

    void AddContextInformation(DependencyObject* pContext, DependencyProperty* pProperty);
    virtual void AddInheritanceContext(DependencyObject* pContext, DependencyProperty* pProperty) AVUI_OVERRIDE;
    void ClearContextAndHandlers();
    void FireChanged();
    bool Freeze(bool isChecking);
    virtual void RemoveInheritanceContext(DependencyObject* pContext, DependencyProperty* pProperty) AVUI_OVERRIDE;
    virtual void Seal() AVUI_OVERRIDE;

#pragma endregion Internal Methods

#pragma region Internal Properties

    virtual bool get_HasMultipleInheritanceContexts() AVUI_OVERRIDE;
    virtual DependencyObject* get_InheritanceContext() AVUI_OVERRIDE;
    bool get_IsFrozenInternal();

#pragma endregion Internal Properties

#pragma region Internal Events

    void add_ChangedInternal(EventHandler* pValue) { AddEventHandler(get_ChangedEvent(), pValue); }
    void remove_ChangedInternal(EventHandler* pValue) { RemoveEventHandler(get_ChangedEvent(), pValue); }

#pragma endregion Internal Events

#pragma region ISealable Properties

    virtual bool get_CanSeal() AVUI_OVERRIDE { return get_CanFreeze(); }
    virtual bool get_IsSealed() AVUI_OVERRIDE { return get_IsFrozen(); }

#pragma endregion ISealable Properties


private:

#pragma region Helpers

    struct FreezableContextPair
    {
        FreezableContextPair() { }
        FreezableContextPair(DependencyObject* pDependObject, DependencyProperty* pDependProperty) : Owner(pDependObject), Property(pDependProperty) { }

        TWeakPtr<Object> Owner;
        DependencyProperty* Property;
    };

#pragma endregion Helpers

#pragma region Private Methods

    void AddContextToList(DependencyObject* pContext, DependencyProperty* pProperty);
    void CloneCoreCommon(Freezable* pSourceFreezable, bool useCurrentValue, bool cloneFrozenValues);
    void EnsureConsistentDispatchers(DependencyObject* pOwner, DependencyObject* pChild);
    void GetChangeHandlersAndInvalidateSubProperties(EventStorage* pCalledHandlers);
    void GetHandlers(EventStorage* pCalledHandlers);
    void PruneContexts();
    void RemoveContextInformation(DependencyObject* pContext, DependencyProperty* pProperty);

#pragma endregion Private Methods
 
#pragma region Private Fields

    ItemStructList<FreezableContextPair> m_inheritanceContexts;

#pragma endregion Private Fields

};

}; // namespace AVUI

